package data;

import java.util.BitSet;
import java.util.Random;

import util.MyBitSet;

public class Key {

	/**
	 * toute la clef en comptant les bits de vérification de parité
	 */
	private MyBitSet key;

	private final int size = 64;
	@SuppressWarnings("unused")
	private final int utilSize = 56;

	private static final int NB_WEAK_KEYS = 4;

	private static final MyBitSet[] weakKeys = {
			// adding weakKey = 01010101 01010101
			new MyBitSet("0000 0001  0000 0001" + "0000 0001  0000 0001"
					+ "0000 0001  0000 0001" + "0000 0001  0000 0001"
					+ "0000 0001  0000 0001" + "0000 0001  0000 0001"
					+ "0000 0001  0000 0001" + "0000 0001  0000 0001"),
			// adding weakKey = FEFEFEFE FEFEFEFE
			new MyBitSet("1111 1110  1111 1110" + "1111 1110  1111 1110"
					+ "1111 1110  1111 1110" + "1111 1110  1111 1110"
					+ "1111 1110  1111 1110" + "1111 1110  1111 1110"
					+ "1111 1110  1111 1110" + "1111 1110  1111 1110"),
			// adding weakKey = E0E0E0E0 F1F1F1F1
			new MyBitSet("1110 0000  1110 0000" + "1110 0000  1110 0000"
					+ "1110 0000  1110 0000" + "1110 0000  1110 0000"
					+ "1111 0001  1111 0001" + "1111 0001  1111 0001"
					+ "1111 0001  1111 0001" + "1111 0001  1111 0001"),
			// adding weakKey = 1F1F1F1F 0E0E0E0E
			new MyBitSet("0001 1111  0001 1111" + "0001 1111  0001 1111"
					+ "0001 1111  0001 1111" + "0001 1111  0001 1111"
					+ "0000 1110  0000 1110" + "0000 1110  0000 1110"
					+ "0000 1110  0000 1110" + "0000 1110  0000 1110")

	};

	/**
	 * 
	 * @param bs
	 */
	public Key(MyBitSet key) {
		if (key.getSize() != size) {
			throw new WrongSizeException();
		}
		boolean equalsAweakKeys = false;
		int i = 0;
		while (i < NB_WEAK_KEYS && !equalsAweakKeys) {
			if (key.equals(weakKeys[i])) {
				equalsAweakKeys = true;
			}
			i++;
		}
		if (equalsAweakKeys) {
			throw new WeakKeyException();
		}
		this.key = key;
	}

	/**
	 * 
	 * @param bs
	 */
	public Key(Key key) {
		this(key.key.clone());
	}

	/**
	 * genere une clef différente des weakKeys
	 * 
	 * 
	 */
	public Key() {
		Random r = new Random();
		key = new MyBitSet(size);
		boolean continu = true;
		while (continu) {
			boolean parity = true;
			@SuppressWarnings("unused")
			int j = 0;
			for (int i = 0; i < size; i++) {
				if (i % 8 == 7) {
					key.set(i, !parity);
					parity = true;
					j++;
				} else {
					boolean valToSet = false;
					if (r.nextInt(2) == 1) {
						valToSet = true;
					}
					key.set(i, valToSet);
				}
			}

			continu = false;
			int i = 0;
			while (i < NB_WEAK_KEYS && !continu) {
				if (key.equals(weakKeys[i])) {
					continu = true;
				}
				i++;
			}
		}
	}

	public String toString(BitSet bs) {
		String s = new String();
		for (int i = size - 1; i >= 0; i--) {
			s += ((bs.get(i)) ? 1 : 0);
		}
		return s;
	}

	/**
	 * exécute les n première étapes de transformation de la clef
	 * 
	 * @param n
	 * @return la clef n
	 */
	public MyBitSet getKeyFromOriginalKey(int n) {
		MyBitSet permuteKey = key.permute(PC1);
		for (int i = 0; i < n; i++) {
			permuteKey.setShift(leftShifNumber[i]);
		}
		return permuteKey.permute(PC2);

	}

	private static final int[] PC1 = { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50,
			42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44,
			36,

			63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61,
			53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 };

	private static final int[] PC2 = { 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21,
			10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,

			41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34,
			53, 46, 42, 50, 36, 29, 32 };

	private static final int[] leftShifNumber = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2,
			2, 2, 2, 2, 2, 1

	};

	public Key clone() {
		return new Key(this);
	}

	public MyBitSet getValue() {
		return key;
	}

}
